<?php

namespace AI;

use GatewayWorker\Lib\Gateway;
use Mysql;
use Util\AsyncHttpUtil;
use Util\LogUtil;

abstract class  IAI
{
    public static string $gptName = "小星";

    public static array $aiMap = [];

    public static function getAiObj(string $aiClass)
    {
        if (!isset(self::$aiMap[$aiClass])) {
            self::$aiMap[$aiClass] = new $aiClass();
        }
        return self::$aiMap[$aiClass];
    }

    public static function getAI(string $userID)
    {
        $aiClass = self::getAiClass($userID);
        return self::getAiObj($aiClass);
    }

    private static function getAiClass(string $userID): string
    {
        $ai_class = Mysql::getDb()
            ->select('ai_class')->from('ai')
            ->where('wx_id= :wx_id')
            ->bindValues(array('wx_id' => "$userID"))
            ->single();
        if (!$ai_class) {
            return ChatGpt::class;
        }
        return $ai_class;
    }

    private static function changeAiClass(string $userID, string $content): void
    {
        if (str_contains($content, "切换#baidu")) {
            $aiClass = BaiduAi::class;
        } else {
            $aiClass = ChatGpt::class;
        }

        \Mysql::getDb()->delete("ai")
            ->where('wx_id= :wx_id')
            ->bindValues(array('wx_id' => "$userID"))
            ->query();

        \Mysql::getDb()->insert("ai")
            ->cols([
                "ai_class" => $aiClass,
                "wx_id" => $userID
            ])
            ->query();
    }

    abstract public function getUrl(): string;

    abstract public function getData(array $message, string $userID): mixed;

    public function getHeader(): array
    {
        return [
            'Content-Type' => "application/json",
            'Connection' => 'keep-alive',
        ];
    }

    protected function saveQuestion($userID, $content): void
    {
        // 保存提问
        self::saveMessageByUid($userID, [
            "role" => "user",
            "content" => $content
        ]);
    }

    protected function saveReply($userID, $content): void
    {
        // 保存回复
        self::saveMessageByUid($userID, [
            "role" => "assistant",
            "content" => $content
        ]);
    }

    abstract function getReplyMsg(string $reply): false|string;

    private function isChangeAiClass($reply): bool
    {
        return str_contains($reply, "切换#");
    }

    final public function reply(array $message, string $client_id, string $userID): void
    {
        // 如果 @了当前 client_id 对应的微信号，就回复消息
        if (!self::canGetReply($message)) {
            return;
        }

        $content = str_replace("@" . self::$gptName, "", $message["content"]);
        if (self::isChangeAiClass($message["content"])) {
            self::changeAiClass($userID, $message["content"]);
            Gateway::sendToClient($client_id, json_encode([
                "type" => 1,
                "msg" => "切换成功",
                "wxid" => $message["talker"]
            ]));
            return;
        }

        AsyncHttpUtil::postJson(
            $this->getUrl(),
            $this->getData($message, $userID),
            function ($response) use ($content, $userID, $message, $client_id) {
                $msg = $this->getReplyMsg($response);
                LogUtil::debug($msg);

                if (!$msg) {
                    self::clearMessageByUid($userID);
                    $msg = "我炸了，已经重置了。请重新开始吧！";
                }else{
                    $this->saveQuestion($userID, $content);
                    $this->saveReply($userID, $msg);
                }



                if ($nickName = self::getGroupNickName($message)) {
                    $msg = "@$nickName " . $msg;
                }
                Gateway::sendToClient($client_id, json_encode([
                    "type" => 1,
                    "msg" => $msg,
                    "wxid" => $message["talker"]
                ]));
            },
            null,
            $this->getHeader()
        );

    }

    public static function isGroupMsg($message): bool
    {
        return isset($message["talker"]) && str_contains($message["talker"], "@chatroom");
    }


    private static function canGetReply(array $message): bool
    {
        $talker = $message["talker"];
        // 群
        if (self::isGroupMsg($message)) {
            $white_array = [
                "34532798585@chatroom" // 孔爷
                , "38773517166@chatroom" // 吃饭小队
                , "39049363855@chatroom" // robot
                , "6640622334@chatroom" //兄弟圈
            ];
            return in_array($talker, $white_array) && str_contains($message["content"], self::$gptName);
        }

        return true;

    }

    private static function getGroupNickName($message)
    {
        return $message["sendByNickName"];
    }


    protected static function getMessagesByUid(string $userID, string $content): array
    {
        $messages = Mysql::getDb()->select('role,content,id')
            ->from('gpt_history')
            ->where('wx_id= :wx_id')
            ->limit(20)
            ->orderByDESC(['id'])
            ->bindValues(array('wx_id' => $userID))
            ->query();

        // 对 messages 进行排序 按照 id 从小到大
        $map = [];
        $messageCount = 0;
        foreach ($messages as $message) {
            $id = $message["id"];
            unset($message["id"]);
            $messageCount += strlen($message["content"]);
            if ($messageCount > 1500) {
                break;
            }
            $map[$id] = $message;
        }
        ksort($map);
        $res = array_values($map);

        $res[] = [
            "role" => "user",
            "content" => $content
        ];
        return $res;
    }

    protected static function saveMessageByUid(string $userID, array $message): void
    {
        Mysql::getDb()->insert('gpt_history')->cols([
            'wx_id' => $userID,
            'role' => $message["role"],
            'content' => $message["content"]
        ])->query();
    }

    protected static function clearMessageByUid(string $userID): void
    {
        Mysql::getDb()->delete("gpt_history")
            ->where('wx_id= :wx_id')
            ->bindValues(array('wx_id' => "$userID"))
            ->query();
    }
}